/* Copyright 2009 Requirements Management System 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.mags.remas.app;

import java.sql.Connection;
import java.util.ArrayList;   
import java.util.List;
import org.apache.commons.dbcp.BasicDataSource; 
import org.apache.commons.validator.GenericTypeValidator;
import org.apache.commons.validator.GenericValidator;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PlatformUI;
import org.mags.common.EngineContext;
import org.mags.remas.exception.SingletonExceptionHandler;
import org.mags.remas.model.commons.DataSourceInformation;
import org.mags.remas.model.commons.checker.AbstractDataBaseChecker;
import org.mags.remas.model.commons.checker.DerbyDataBaseChecker;
import org.mags.remas.model.commons.checker.ScriptReader;
import org.mags.remas.view.commons.ImageProvider;
import org.mags.remas.view.dialogs.DataSourceConfigurationDialog;
import org.mags.remas.view.dialogs.ProgressBarDialog;

/**
 * 
 * @author Mario Gaitan
 *
 */
public class Application implements IApplication {
	private Throwable exception;
	public int getVersion(String script, String search) {
		if(!GenericValidator.isBlankOrNull(script)) {
			int index = script.indexOf(search);
			if(index>=0) {
				int length = search.length();
				index = script.indexOf("=", index+length);
				int startIndex = index+1;
				int endIndex = script.indexOf(">", index+1);
				String version = script.substring(startIndex, endIndex);
				version = version.trim();
				Integer _version = GenericTypeValidator.formatInt(version);
				if(_version==null)
					return -1;
				else
					return _version;
			}
		}
		return -1;
	}
	public boolean isMajor(int major, int minor, int micro, AbstractDataBaseChecker checker, BasicDataSource dataSource) {
		Connection connection = null;
		try {
			connection = dataSource.getConnection();
			return checker.getSQLUtils(connection).isMajor(major, minor, micro);
		} catch(Exception e) {
			/*IGNORE EXCEPTION*/
		} finally {
			try {
				if(connection!=null)
					connection.close();
			} catch(Exception e) {
				/*IGNORE EXCEPTION*/
			}
		}
		return false;
	}
	public Throwable checkDataBase(final Display display, final AbstractDataBaseChecker checker, final BasicDataSource dataSource) {
		final boolean showException[] = new boolean[1];
		showException[0] = true;
		ProgressBarDialog progressDialog = new ProgressBarDialog(display) {
			private boolean[] execute = new boolean[]{false, true, false, false, false};
			@Override
			protected String process(int times) {
				String message = "testing connection";
				if(execute[times]) {
					if(times==1) {
						exception = checker.testConnection(dataSource);
						if(exception!=null) {
							String key = SingletonExceptionHandler.getInstance().getKey(exception);
							if(!key.equals("invalidAuthentication")) {
				                display.syncExec(new  Runnable()  {
				                    public  void  run()  {
				                    	if(SingletonExceptionHandler.getInstance().showExceptionAndQuestion(exception, "Do you want to try create database?"))  
				                    		execute[3] = true;
				                    	else
				                    		showException[0] = false;
				                    }
				                });
							}
							if(execute[3])
								message = "trying to build database";
						} else {
							execute[2] = true;
							message = "updating database";
						}
					}
					else if(times==2) {
						String script = ScriptReader.readScript(checker.getDriverName()+".update.ftl");
						int major = getVersion(script, "___versionMajor");
						int minor = getVersion(script, "___versionMinor");
						int micro = getVersion(script, "___versionMicro");
						if(major>-1&&minor>-1&&micro>-1
						&& isMajor(major, minor, micro, checker, dataSource)) {
							final boolean upgrade[] = new boolean[1];
							upgrade[0] = false;
			                display.syncExec(new  Runnable()  {
			                    public  void  run()  {
									if(MessageDialog.openQuestion(null, "WARNING", "Database version is old, application needs another version, Do you want upgrade?"))
										upgrade[0] = true;
			                    }
			                });		
			                if(upgrade[0])
								exception = checker.updateDataStructure(dataSource);
						}
					}
					else if(times==3) {
						exception = checker.createDatabase(dataSource);
						if(exception==null) {
							execute[4] = true;
							message="trying to build structure";
						}
					}
					else if(times==4)
						exception = checker.createDataStructure(dataSource);
				}
				return message;
			}
		};
        progressDialog.setExecuteTime(4);
        progressDialog.setMayCancel(false);
        progressDialog.setText("processing");
        progressDialog.setProcessMessage("please wait...");
        progressDialog.setProcessImage(ImageProvider.getCommonImage("process"));
        progressDialog.setImage(ImageProvider.getCommonImage("process"));
        checker.setShowProgress(progressDialog);
        progressDialog.show();
		if(exception!=null&&showException[0])
			SingletonExceptionHandler.getInstance().showException(exception);
		return exception;
	}
	protected void loadCheckers(List<AbstractDataBaseChecker> checkers) {
		checkers.add(new DerbyDataBaseChecker());
	}
	public Object start(IApplicationContext context) throws Exception {
		Window.setExceptionHandler(new Window.IExceptionHandler() {
			@Override
			public void handleException(Throwable t) {
				SingletonExceptionHandler.getInstance().showException(t);
			}
		});
		Display display = PlatformUI.createDisplay();
		List<AbstractDataBaseChecker> checkers = new ArrayList<AbstractDataBaseChecker>();
		loadCheckers(checkers);
		try {
			Throwable exception = null;
			do {
				DataSourceInformation dataSourceInformation =null;
				DataSourceConfigurationDialog dialog = new DataSourceConfigurationDialog(display);
				dialog.show();
				dataSourceInformation = dialog.getSelectedDS();
				if(dataSourceInformation==null)
					return IApplication.EXIT_OK;
				EngineContext.getDataContext().setDataSourceInformation(dataSourceInformation);
				BasicDataSource dataSource = EngineContext.getDataContext().getDataSource();
				AbstractDataBaseChecker checker=null;
				for(AbstractDataBaseChecker i: checkers)
					if(i.equalsDriverName(dataSourceInformation.getDriverName())) {
						checker = i;
						break;
					}
				exception = checkDataBase(display, checker, dataSource);
			} while(exception!=null);
			EngineContext.getDataContext().refreshCache();
		} catch(Exception e) {
			SingletonExceptionHandler.getInstance().showException(e);
			return IApplication.EXIT_RESTART;
		}		
		try {
			int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor());
			if (returnCode == PlatformUI.RETURN_RESTART)
				return IApplication.EXIT_RESTART;
			else
				return IApplication.EXIT_OK;
		} finally {
			display.dispose();
		}
		
	}

	/* (non-Javadoc)
	 * @see org.eclipse.equinox.app.IApplication#stop()
	 */
	public void stop() {
		final IWorkbench workbench = PlatformUI.getWorkbench();
		if (workbench == null)
			return;
		final Display display = workbench.getDisplay();
		display.syncExec(new Runnable() {
			public void run() {
				if (!display.isDisposed())
					workbench.close();
			}
		});
	}
}
